/*
 * ******************************************************************************
 *   Copyright (c) 2013-2014 Gabriele Mariotti.
 *
 *   Licensed under the Apache License, Version 2.0 (the "License");
 *   you may not use this file except in compliance with the License.
 *   You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 *   Unless required by applicable law or agreed to in writing, software
 *   distributed under the License is distributed on an "AS IS" BASIS,
 *   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *   See the License for the specific language governing permissions and
 *   limitations under the License.
 *  *****************************************************************************
 */

package it.gmariotti.cardslib.library.prototypes;


import it.gmariotti.cardslib.library.Constants;
import it.gmariotti.cardslib.library.ResourceTable;
import it.gmariotti.cardslib.library.internal.CardArrayAdapter;
import ohos.agp.components.*;
import ohos.agp.database.DataSetSubscriber;
import ohos.app.Context;
import ohos.hiviewdfx.HiLog;
import ohos.utils.PlainArray;

import java.util.Arrays;
import java.util.Comparator;

/**
 * An adapter to build a CardList with sections.
 *
 * @author Gabriele Mariotti (gabri.mariotti@gmail.com)
 */
public class SectionedCardAdapter extends RecycleItemProvider {

    private boolean mValid = true;

    /**
     * Default layout used for sections
     */
    private int mSectionResourceId = ResourceTable.Layout_base_section_layout;

    /**
     * Inflater
     */
    private LayoutScatter mLayoutInflater;

    /**
     * Adapter with cards.
     */
    private BaseItemProvider mBaseAdapter;

    /**
     * Array with Card Sections
     */
    private PlainArray<CardSection> mCardSections = new PlainArray<>();

    // -------------------------------------------------------------
    // Constructors
    // -------------------------------------------------------------

    /**
     * Constructor
     *
     * @param context
     * @param cardArrayAdapter
     */
    public SectionedCardAdapter(Context context, CardArrayAdapter cardArrayAdapter) {
        this(context, ResourceTable.Layout_base_section_layout, cardArrayAdapter);
    }

    /**
     * @param context           context
     * @param sectionResourceId layout used by sections
     * @param cardArrayAdapter  cardArrayAdapter
     */
    public SectionedCardAdapter(Context context, int sectionResourceId, CardArrayAdapter cardArrayAdapter) {
        mLayoutInflater = LayoutScatter.getInstance(context);
        mBaseAdapter = cardArrayAdapter;
        mSectionResourceId = sectionResourceId;
        mBaseAdapter.addDataSubscriber(new DataSetSubscriber() {
            @Override
            public void onChanged() {
                super.onChanged();
                mValid = mBaseAdapter.getCount() != 0;
                notifyDataChanged();
            }

            @Override
            public void onInvalidated() {
                super.onInvalidated();
                mValid = false;
                notifyDataInvalidated();
            }
        });
    }

    // -------------------------------------------------------------
    // Section
    // -------------------------------------------------------------

    /**
     * Sets the Card sections
     *
     * @param cardSections
     */
    public void setCardSections(CardSection[] cardSections) {
        mCardSections.clear();

        Arrays.sort(cardSections, Comparator.comparingInt(o -> o.firstPosition));

        int offset = 0; // offset positions for the headers we're adding
        for (CardSection cardSection : cardSections) {
            cardSection.sectionedPosition = cardSection.firstPosition + offset;
            mCardSections.append(cardSection.sectionedPosition, cardSection);
            ++offset;
        }

        notifyDataChanged();
    }

    // -------------------------------------------------------------
    // Adapter's methods
    // -------------------------------------------------------------

    /**
     * Returns the sectioned position from the position
     *
     * @param position
     * @return int
     */
    public int positionToSectionedPosition(int position) {
        int offset = 0;
        for (int i = 0; i < mCardSections.size(); i++) {
            if (mCardSections.valueAt(i).firstPosition > position) {
                break;
            }
            ++offset;
        }
        return position + offset;
    }

    /**
     * Returns the position from the sectioned position
     *
     * @param sectionedPosition
     * @return int
     */
    public int sectionedPositionToPosition(int sectionedPosition) {
        if (isSectionHeaderPosition(sectionedPosition)) {
            return ListContainer.INVALID_INDEX;
        }

        int offset = 0;
        for (int i = 0; i < mCardSections.size(); i++) {
            if (mCardSections.valueAt(i).sectionedPosition > sectionedPosition) {
                break;
            }
            --offset;
        }
        return sectionedPosition + offset;
    }

    /**
     * Returns true if the position is a Section
     *
     * @param position
     * @return boolean
     */
    public boolean isSectionHeaderPosition(int position) {
        return mCardSections.get(position).isPresent();
    }

    @Override
    public int getCount() {
        return (mValid ? mBaseAdapter.getCount() + mCardSections.size() : 0);
    }

    @Override
    public Object getItem(int position) {
        return isSectionHeaderPosition(position)
                ? mCardSections.get(position)
                : mBaseAdapter.getItem(sectionedPositionToPosition(position));
    }

    @Override
    public long getItemId(int position) {
        return isSectionHeaderPosition(position)
                ? Integer.MAX_VALUE - mCardSections.indexOfKey(position)
                : mBaseAdapter.getItemId(sectionedPositionToPosition(position));
    }

    @Override
    public Component getComponent(int position, Component component, ComponentContainer componentContainer) {
        if (isSectionHeaderPosition(position)) {
            return internalSectionView(position, component, componentContainer);
        } else {
            return mBaseAdapter.getComponent(sectionedPositionToPosition(position), component, componentContainer);
        }
    }

    @Override
    public int getItemComponentType(int position) {
        return isSectionHeaderPosition(position)
                ? getComponentTypeCount() - 1
                : mBaseAdapter.getItemComponentType(sectionedPositionToPosition(position));
    }

    //todo no such method
//    @Override
//    public boolean isEnabled(int position) {
//        //noinspection SimplifiableConditionalExpression
//        return isSectionHeaderPosition(position)
//                ? false
//                : mBaseAdapter.isEnabled(sectionedPositionToPosition(position));
//    }

    @Override
    public int getComponentTypeCount() {
        return mBaseAdapter.getComponentTypeCount() + 1; // the section headings
    }

    //todo mo such method
//    @Override
//    public boolean areAllItemsEnabled() {
//        return false;
//    }
//
//    @Override
//    public boolean hasStableIds() {
//        return mBaseAdapter.hasStableIds();
//    }


    /**
     * Returns the view used by the section at position
     *
     * @param position    section's position
     * @param convertView
     * @param parent
     * @return Component
     */
    protected Component internalSectionView(int position, Component convertView, ComponentContainer parent) {
        Component view = convertView;
        if (view == null) {
            view = mLayoutInflater.parse(mSectionResourceId, parent, false);
        }
        getSectionView(position, view, parent);
        return view;
    }

    /**
     * Returns the view used by the section at position.
     * Override this method to set your ui elements.
     *
     * @param position section's position
     * @param view
     * @param parent
     * @return Component
     */
    protected Component getSectionView(int position, Component view, ComponentContainer parent) {

        Text textView = (Text) view.findComponentById(ResourceTable.Id_card_section_simple_title);
        if (textView != null)
            textView.setText(mCardSections.get(position).get().title.toString());
        return view;
    }

    /**
     * Use this method to add a single {@link CardSection}.</p>
     * If you want to add more CardSections use the method {@link #addCardSections(CardSection[])}
     *
     * @param cardSection to be added
     */
    public void addCardSection(CardSection cardSection) {

        if (cardSection != null) {
            int oldSize = mCardSections.size();
            CardSection[] newCardSections = new CardSection[oldSize + 1];

            //Get current sections
            for (int i = 0; i < mCardSections.size(); i++) {
                newCardSections[i] = mCardSections.valueAt(i);
            }
            //Add new section
            newCardSections[oldSize] = cardSection;
            setCardSections(newCardSections);
        }
    }

    /**
     * Adds card Sections
     *
     * @param cardSections card sections to be added
     */
    public void addCardSections(CardSection[] cardSections) {

        if (cardSections != null && cardSections.length > 0) {

            int oldSize = mCardSections.size();

            //Get current sections
            CardSection[] newCardSections = new CardSection[oldSize + cardSections.length];
            for (int i = 0; i < mCardSections.size(); i++) {
                newCardSections[i] = mCardSections.valueAt(i);
            }

            //Add new sections
            for (int i = 0; i < cardSections.length; i++) {
                newCardSections[i + oldSize] = cardSections[i];
            }

            setCardSections(newCardSections);
        }
    }

    // -------------------------------------------------------------
    // Getters and setters
    // -------------------------------------------------------------

    /**
     * Returns the sections
     *
     * @return PlainArray<CardSection>
     */
    public PlainArray<CardSection> getCardSections() {
        return mCardSections;
    }

}
